package com.atguigu.gmall.search.service.impl;

import com.atguigu.gmall.model.list.*;
import com.atguigu.gmall.search.dao.GoodsEsRepository;
import com.atguigu.gmall.search.service.SkuEsService;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.Nested;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.List;
import java.util.Optional;

@Service
public class SkuEsServiceImpl implements SkuEsService {

    @Autowired
    GoodsEsRepository goodsEsRepository;


    @Autowired
    ElasticsearchRestTemplate restTemplate;


    @Override
    public void save(Goods goods) {
        goods.setCreateTime(new Date()); //更新处理日期
        goodsEsRepository.save(goods);
    }

    @Override
    public void deleteById(Long skuId) {
        goodsEsRepository.deleteById(skuId);
    }

    @Override
    public SearchResponseVo search(SearchParam searchParam) {

        //1、根据请求参数构造出 真正的DSL语句（查询条件对象）
        Query query = buildQuery(searchParam);

        //ES远程检索的结果
        SearchHits<Goods> goods = restTemplate.search(query, Goods.class, IndexCoordinates.of("goods"));

        //2、把es的检索结果封装成responseVo
        SearchResponseVo vo = buildResponse(goods,searchParam);

        return vo;
    }

    @Override
    public void updateSkuHotScore(Long skuId, Long hotScore) {
        Optional<Goods> byId = goodsEsRepository.findById(skuId);

        Goods goods = byId.get();
        if(goods!=null){
            goods.setHotScore(hotScore);
        }
        goodsEsRepository.save(goods);
    }


    /**
     * 根据查询结果构建出页面的VO
     * @param responseDSL
     * @return
     */
    private SearchResponseVo buildResponse(SearchHits<Goods> responseDSL,SearchParam searchParam) {
        SearchResponseVo responseVo = new SearchResponseVo();
        //1、构建响应的商品集合
        List<Goods> goodsList = responseVo.getGoodsList();
        List<SearchHit<Goods>> searchHits = responseDSL.getSearchHits();
        for (SearchHit<Goods> hit : searchHits) {
            Goods content = hit.getContent();
            //获取查询到的商品
            List<String> title = hit.getHighlightField("title");
            if(!CollectionUtils.isEmpty(title)){
                //商品标题为高亮显示
                content.setTitle(title.get(0));
            }
            goodsList.add(content);
        }
        //2、构建响应的分页信息
        long totalHits = responseDSL.getTotalHits();
        responseVo.setTotal(totalHits);
        responseVo.setPageNo(searchParam.getPageNo());
        responseVo.setPageSize(searchParam.getPageSize());
//  总：10  每页：3  总页码：   10/3 = 3.333  向上取整
        Long totalPages = totalHits % searchParam.getPageSize() == 0 ? (totalHits / searchParam.getPageSize()) : (totalHits / searchParam.getPageSize() + 1);
        responseVo.setTotalPages(totalPages);

        //3、构建响应的品牌列表【根据聚合分析】
        List<SearchResponseTmVo> trademarkList = responseVo.getTrademarkList();
        if (responseDSL.hasAggregations()){
            //有聚合内容
            Aggregations aggregations = responseDSL.getAggregations();
            //获取品牌的聚合结果
            Terms tmAgg = aggregations.get("tmAgg");
            //所有的品牌
            List<? extends Terms.Bucket> buckets = tmAgg.getBuckets();
            if (!StringUtils.isEmpty(buckets)){
                for (Terms.Bucket bucket : buckets) {
                    //遍历一个品牌
                    SearchResponseTmVo tmVo = new SearchResponseTmVo();
                    //设置品牌id
                    tmVo.setTmId(bucket.getKeyAsNumber().longValue());
                    //设置品牌名字
                    Terms tmNameAgg = bucket.getAggregations().get("tmNameAgg");
                    Terms.Bucket nameBucket = tmNameAgg.getBuckets().get(0);
                    tmVo.setTmName(nameBucket.getKeyAsString());
                    //设置品牌Url
                    Terms tmUrlAgg = nameBucket.getAggregations().get("tmUrlAgg");
                    String url = tmUrlAgg.getBuckets().get(0).getKeyAsString();
                    tmVo.setTmLogoUrl(url);
                    trademarkList.add(tmVo);
                }
            }

        }
        //4、构建响应的属性列表【根据聚合分析】
        List<SearchResponseAttrVo> attrsList = responseVo.getAttrsList();
        if (responseDSL.hasAggregations()){
            Nested attrAgg = responseDSL.getAggregations().get("attrAgg");
            Terms attrIdAgg = attrAgg.getAggregations().get("attrIdAgg");
            if (!StringUtils.isEmpty(attrIdAgg.getBuckets())){
                for (Terms.Bucket bucket : attrIdAgg.getBuckets()) {
                    //遍历封装每一个属性信息
                    SearchResponseAttrVo attrVo = new SearchResponseAttrVo();
                    //封装属性id
                    attrVo.setAttrId(bucket.getKeyAsNumber().longValue());

                    //封装属性名字
                    Terms attrNameAgg = bucket.getAggregations().get("attrNameAgg");
                    Terms.Bucket attrNameBucket = attrNameAgg.getBuckets().get(0);
                    attrVo.setAttrName(attrNameBucket.getKeyAsString());


                    //封装属性值的可能性
                    List<String> attrValueList = attrVo.getAttrValueList();
                    Terms valueAgg = attrNameBucket.getAggregations().get("attrValueAgg");
                    for (Terms.Bucket valueAggBucket : valueAgg.getBuckets()) {
                        //添加属性值的1种可能
                        attrValueList.add(valueAggBucket.getKeyAsString());
                    }

                    attrsList.add(attrVo);
                }
            }
        }

        return responseVo;
    }


    /**
     * 1、按照请求参数构建出dsl条件
     * @param searchParam
     * @return
     */
    private Query buildQuery(SearchParam searchParam){

        //动态判断构建条件......
        //遇见复杂对象，优先找  xxxxBuilder。
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();

        //1.查询条件
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        if (!StringUtils.isEmpty(searchParam.getKeyword())){
            //按照title模糊检索
            boolQuery.must(QueryBuilders.matchQuery("title",searchParam.getKeyword()));
        }
        if (searchParam.getCategory3Id() != null){
            //按照三级分类进行过滤查询
            boolQuery.filter(QueryBuilders.termQuery("category3Id",searchParam.getCategory3Id()));
        }
        if (!StringUtils.isEmpty(searchParam.getTrademark())){
            //按照品牌进行查询
            //trademark=1:小米
            String[] split = searchParam.getTrademark().split(":");
            boolQuery.filter(QueryBuilders.termQuery("tmId",split[0]));
        }

        //属性都是动态
        if (searchParam.getProps() != null && searchParam.getProps().length > 0){
            //按照属性进行检索
            //props=407:Android:系统1&props=412:4G:内存
            for (String prop : searchParam.getProps()) {
                String[] split = prop.split(":");
                BoolQueryBuilder query = QueryBuilders.boolQuery();
                query.must(QueryBuilders.termQuery("attrs.attrId",split[0]));
                query.must(QueryBuilders.termQuery("attrs.attrValue",split[1]));
                NestedQueryBuilder attrs = QueryBuilders.nestedQuery("attrs", query, ScoreMode.None);
                boolQuery.filter(attrs);
            }
        }
        //所有查询条件搞定
        builder.withQuery(boolQuery);

        //2.分页条件
        if (searchParam.getPageNo() != null){
            Integer pageSize = searchParam.getPageSize();
//            PageRequest pageRequest = new PageRequest(searchParam.getPageNo() - 1, pageSize);
            //这里的页码是从0开始的
            PageRequest of = PageRequest.of(searchParam.getPageNo() - 1, pageSize);
            builder.withPageable(of);
        }

        //3.排序条件
        if (!StringUtils.isEmpty(searchParam.getOrder())){
            //order=1:desc  1:按照热度排序    2:按照价格排序
            String[] split = searchParam.getOrder().split(":");
            if ("1".equals(split[0])){
                //1.按照热度排序
                FieldSortBuilder order = SortBuilders.fieldSort("hotScore").order(split[1].equals("asc") ? SortOrder.ASC : SortOrder.DESC);
                builder.withSort(order);
            }else {
                //2.按照价格排序
                FieldSortBuilder order = SortBuilders.fieldSort("price").order(split[1].equals("asc") ? SortOrder.ASC : SortOrder.DESC);
                builder.withSort(order);
            }
        }else {
            //前端没带默认就是热度降序
            FieldSortBuilder order = SortBuilders.fieldSort("hotScore").order(SortOrder.DESC);
            builder.withSort(order);
            searchParam.setOrder("1:desc");
        }

        //4.高亮条件
        if (!StringUtils.isEmpty(searchParam.getKeyword())){
            HighlightBuilder.Field field = new HighlightBuilder.Field("title");
            field.preTags("<span style='color:red'>");
            field.postTags("</span>");
            builder.withHighlightFields(field);
        }

        //5.聚合条件
        //5.1）、按照品牌聚合
        TermsAggregationBuilder tmAgg =
                AggregationBuilders.terms("tmAgg").field("tmId").size(100)
                        .subAggregation(
                                AggregationBuilders.terms("tmNameAgg").field("tmName").size(1)
                                        .subAggregation(AggregationBuilders.terms("tmUrlAgg").field("tmLogoUrl").size(1))
                        );
        //5.1.1）、tmId的聚合
        //5.1.2）、子聚合名字
        //5.1.2.1）、子聚合路径
        builder.addAggregation(tmAgg);


        //5.2）、按照属性聚合
        NestedAggregationBuilder nested =
                AggregationBuilders.nested("attrAgg", "attrs")
                        .subAggregation( AggregationBuilders
                                .terms("attrIdAgg").field("attrs.attrId").size(100)
                                .subAggregation(
                                        AggregationBuilders.terms("attrNameAgg").field("attrs.attrName").size(1)
                                                .subAggregation(
                                                        AggregationBuilders.terms("attrValueAgg").field("attrs.attrValue").size(100)
                                                )
                                ));
        builder.addAggregation(nested);
        String s = builder.toString();
        System.out.println("DSL语句："+s);
        return builder.build();
    }



}
